package server

import (
	"encoding/binary"
	"errors"
	"fmt"
	"gitee.com/yuya000/cards-game-server-go/logwarp"
	"github.com/vmihailenco/msgpack"
	"io"
	"time"
)

const (
	CommonHeaderSize        = 14
	MessageTypeCreateGame   = 1003 // 创建游戏
	MessageTypeJoinGame     = 1004 // 加入游戏
	MessageTypeResumeGame   = 1023 // 玩家重新加入游戏
	MessageTypeLostConn     = 5001 // 玩家掉线
	MessageTypePing         = 5000 // ping
	MessageTypePong         = 5002 // pong
	MessageTypeTalk         = 5160 // 聊天消息
	MessageTypeGameType     = 9000 // 游戏类型接口
	MessageTypePlayerResume = 1023 // 玩家重新加入游戏
)

type CommonHeader struct {
	// 消息体总字节数 4 byte
	TotalSize uint32
	// 消息类型 2 byte
	MessageType uint16
	// 通用消息体字节数 4 byte
	CommonBodySize uint32
	// 数据消息体字节数 4 byte
	DataBodySize uint32
}

type Req struct {
	Token string
}

type Resp struct {
	Code      int
	Msg       string
	Timestamp int64
}

type Message struct {
	Header CommonHeader
	Req    Req
	Data   []byte
}

func ReadCommonHeader(reader io.Reader, h *CommonHeader) error {
	if err := binary.Read(reader, binary.BigEndian, h); err != nil {
		return err
	}
	if err := h.verify(); err != nil {
		return err
	}
	return nil
}

func (header CommonHeader) verify() error {
	if header.TotalSize <= 0 {
		return errors.New("TotalSize <= 0")
	}
	if header.CommonBodySize <= 0 {
		return errors.New("CommonBodySize <= 0")
	}
	if header.TotalSize != CommonHeaderSize+header.CommonBodySize+header.DataBodySize {
		return fmt.Errorf("header.TotalSize != CommonHeaderSize+header.CommonBodySize+header.DataBodySize")
	}
	return nil
}

func ReadMessage(reader io.Reader) (*Message, error) {
	var err error
	var msg Message
	if err = ReadCommonHeader(reader, &msg.Header); err != nil {
		return nil, err
	}
	commonBodyBuffer := make([]byte, msg.Header.CommonBodySize)
	if err := binary.Read(reader, binary.BigEndian, commonBodyBuffer); err != nil {
		return nil, err
	}
	if err := msgpack.Unmarshal(commonBodyBuffer, &msg.Req); err != nil {
		return nil, err
	}
	if msg.Header.DataBodySize == 0 {
		return &msg, err
	}
	msg.Data = make([]byte, msg.Header.DataBodySize)
	if err := binary.Read(reader, binary.BigEndian, msg.Data); err != nil {
		return nil, err
	}
	if msg.Header.MessageType != 5000 {
		logwarp.Std().Debugln("请求", msg.Header)
		logwarp.Std().Debugln("请求", msg.Req)
		logwarp.Std().Debugln("请求", msg.Data)
	}
	return &msg, err
}

func SendOK(session *Session, a interface{}, mt uint16) error {
	return Send(session, mt, &Resp{
		Code:      200,
		Msg:       "ok",
		Timestamp: time.Now().Unix(),
	}, a)
}

func SendException(session *Session, ex Exception, mt uint16) error {
	return Send(session, mt, &Resp{
		Code:      ex.Code(),
		Msg:       ex.Msg(),
		Timestamp: time.Now().Unix(),
	}, nil)
}

func Send(session *Session, mt uint16, resp *Resp, a interface{}) error {
	var err error
	header := &CommonHeader{
		MessageType: mt,
	}
	respBuffer, err := msgpack.Marshal(resp)
	if err != nil {
		return err
	}
	header.CommonBodySize = uint32(len(respBuffer))
	var dataBodyBuffer []byte
	if a != nil {
		dataBodyBuffer, err = msgpack.Marshal(a)
		if err != nil {
			return err
		}
		header.DataBodySize = uint32(len(dataBodyBuffer))
	}
	header.TotalSize = CommonHeaderSize + header.CommonBodySize + header.DataBodySize

	if header.MessageType != 5002 {
		logwarp.Std().Debugln("响应", header)
		logwarp.Std().Debugln("响应", respBuffer)
		logwarp.Std().Debugln("响应", dataBodyBuffer)
	}

	if err := binary.Write(session, binary.BigEndian, header); err != nil {
		return err
	}
	if err := binary.Write(session, binary.BigEndian, respBuffer); err != nil {
		return err
	}
	if a != nil {
		if err := binary.Write(session, binary.BigEndian, dataBodyBuffer); err != nil {
			return err
		}
	}
	return nil
}
